package multidraw;

import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.BufferedInputStream;
import java.io.BufferedWriter;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.ObjectStreamClass;
import java.net.URL;
import java.rmi.RMISecurityManager;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.List;
import java.util.Vector;

import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.ImageIcon;
import javax.swing.JApplet;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JToolBar;
import javax.swing.filechooser.FileFilter;
import javax.swing.filechooser.FileNameExtensionFilter;

import multidraw.dialog.ShareToolDialog;
import multidraw.model.DrawingCanvasModel;
import multidraw.model.RectangleShape;
import multidraw.model.Shape;
import multidraw.model.ToolUpdateObserver;
import multidraw.model.UserStatusObserver;
import multidraw.model.commands.AddShapeCommand;
import multidraw.model.commands.AddToolCommand;
import multidraw.model.server.CanvasModel;
import multidraw.tools.DynamicToolLoader;
import multidraw.tools.EraserToolController;
import multidraw.tools.FreehandShapeFactory;
import multidraw.tools.FreehandShapeTool;
import multidraw.tools.LineShapeFactory;
import multidraw.tools.OvalShapeFactory;
import multidraw.tools.RectangleShapeFactory;
import multidraw.tools.SelectorTool;
import multidraw.tools.TextBoxFactory;
import multidraw.tools.TextBoxTool;
import multidraw.tools.Tool;
import multidraw.tools.ToolController;
import multidraw.tools.ToolInfo;
import multidraw.tools.TwoEndShapeTool;
import multidraw.util.ToolArchive;


/**
 * Main class of the program.
 * @author cuongbk
 *
 */
public class MultiDraw1 extends JApplet implements ToolUpdateObserver, UserStatusObserver {

	public static final int DEFAULT_WIDTH = 850;
	public static final int DEFAULT_HEIGHT = 600;
	
	private DrawingCanvasModel model;
	
	private DrawingCanvasView canvas;
	
	private Vector<Object> appToolList;
	
	private Vector<Object> dynToolList;
	
	private JMenuBar menuBar;
	private JMenu toolMenu;
	
	private JToolBar toolBar;
	
	protected ControlPanelView controlPanel;
	private boolean isApplet;
	SecureRandom rand = new SecureRandom();
	
	private ClientListPanelView clientList;
	
	private DynamicToolLoader dynamicToolLoader = new DynamicToolLoader();
	
	 public MultiDraw1(boolean isApplet) { 
		    this.isApplet = isApplet;
		    if (!isApplet) {
		      init();
		    }
		  }

	 public MultiDraw1() {
      /* invoked as Applet */
	    this(true);
	  }
	
	
	public void init() 
	{
		// TODO Auto-generated constructor stub
		setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT);
		
		setFocusable(true);
		
		
		
		//DrawingCanvasModel 
		model = new DrawingCanvasModel();
		
		model.attachToolUpdateObserver(this);
		model.attachUserStatusObserver(this);
		
		model.setToolLoader(dynamicToolLoader);
		
		canvas = new DrawingCanvasView(model);
		
		//build tool list
		appToolList = buildToolList();
		
		dynToolList = dynamicToolLoader.buildDynamicToolList(model, canvas);
		
		//init the menu
		initMenu();
		
		initToolBar();
		
		controlPanel = createControlPanelView();
		controlPanel.setClient(this);
		
		clientList = new ClientListPanelView(model);
		add(clientList, BorderLayout.EAST);
		
		//model.addShape(generateRandomRect());
		//model.addShape(generateRandomRect());
		
		add(canvas);
		add(toolBar, BorderLayout.WEST);
		add(controlPanel, BorderLayout.SOUTH);
	}
	
	private void initMenu() {
		menuBar = new JMenuBar();
		
		JMenu fileMenu = new JMenu("File");
		
		JMenuItem openMenuItem = new JMenuItem("Open...");
		openMenuItem.addActionListener(new OpenHandler());
		fileMenu.add(openMenuItem);
		
		JMenuItem saveMenuItem = new JMenuItem("Save...");
		saveMenuItem.addActionListener(new SaveHandler());
		fileMenu.add(saveMenuItem);
		
		JMenuItem addToolMenuItem = new JMenuItem("Add Tool...");
		addToolMenuItem.addActionListener(new AddToolMenuHandler());
		fileMenu.add(addToolMenuItem);
		
		JMenuItem shareToolMenuItem = new JMenuItem("Share Tool...");
		shareToolMenuItem.addActionListener(new ShareToolMenuHandler());
		fileMenu.add(shareToolMenuItem);
		
		menuBar.add(fileMenu);
		
		toolMenu = new JMenu("Tool");

		for (Object tc: appToolList) {
			toolMenu.add((Action)tc);
		}
		
		for (Object tc: dynToolList) {
			toolMenu.add((Action)tc);
		}
		
		menuBar.add(toolMenu);
					
		setJMenuBar(menuBar);
	}
	
	private void initToolBar() {
		toolBar = new JToolBar();
		
		toolBar.setOrientation(JToolBar.VERTICAL);
		
		for (Object tc: appToolList) {
			toolBar.add((Action)tc);
		}
		
		for (Object tc: dynToolList) {
			toolBar.add((Action)tc);
		}
	}
	
	
	public static void main(String[] args) {	
		System.setProperty("java.security.policy", "policy.all");
		if (System.getSecurityManager() == null) {
			System.setSecurityManager(new RMISecurityManager());
		}
		
	    JFrame frame = new JFrame();
	    frame.setTitle("MultiDraw Fifth Iteration");
	    frame.getContentPane().setLayout(new BorderLayout());
	    MultiDraw1 multidraw = new MultiDraw1(false);
	    frame.getContentPane().add(multidraw,
				      BorderLayout.CENTER);
	    frame.addWindowListener(new AppCloser(multidraw));
	    frame.pack();
	    frame.setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT);
	    frame.setVisible(true);
	}
	
	protected Vector<Object> buildToolList() {
		Vector<Object> toolList = new Vector<Object>(5, 1);
		toolList.add(new ToolController("Selector", getImageIcon("selector.jpg"), 
				"selector tool", model, new SelectorTool(model) ));
		toolList.add(new ToolController("Rectangle", getImageIcon("rectangle.jpg"), 
				"rectangle tool", model, new TwoEndShapeTool(model, new RectangleShapeFactory())));
		toolList.add(new ToolController("Oval", getImageIcon("oval.jpg"), 
				"Oval tool", model, new TwoEndShapeTool(model, new OvalShapeFactory())));
		toolList.add(new ToolController("Line", getImageIcon("line.jpg"), 
				"Line tool", model, new TwoEndShapeTool(model, new LineShapeFactory())));
		toolList.add(new ToolController("Line", getImageIcon("freehand.jpg"), 
				"Freehand tool", model, new FreehandShapeTool(model, new FreehandShapeFactory())));
		toolList.add(new EraserToolController("Eraser", getImageIcon("eraser.jpg"), 
				"Eraser tool", model));
		toolList.add(new ToolController("Text", getImageIcon("text.jpg"), 
				"Text tool", model, new TextBoxTool(model, canvas , new TextBoxFactory())));
		return toolList;
	}
	
	protected ImageIcon getImageIcon(String fileName) {
		//InputStream is2 = MultiDraw1.class.getResource("/"+fileName);
		
	    return new ImageIcon(MultiDraw1.class.getResource("/"+fileName));    
	}
	
	protected DrawingCanvasView createDrawingCanvas() {
		return new DrawingCanvasView(model);
	}
	
	protected ControlPanelView createControlPanelView() {
		return new ControlPanelView(model);
	}
	
	public ClientListPanelView openClientList(String name) {
		
		//repaint();
		//JOptionPane.showConfirmDialog(this, "Login successfully");
		repaint();
		validate();
		
		return clientList;
	}
	
	private class OpenHandler implements ActionListener {
		public void actionPerformed(ActionEvent e) {
			JFileChooser fileChooser = new JFileChooser();
			FileFilter filter = new FileNameExtensionFilter(null, "mdr");
			fileChooser.setFileFilter(filter);
			
			int option = fileChooser.showOpenDialog(getParent());
			if (option == JFileChooser.APPROVE_OPTION) {
				try {
					FileInputStream fis = new FileInputStream(fileChooser.getSelectedFile());
					BufferedInputStream bis = new BufferedInputStream(fis);
					ObjectInputStream ois = new ObjectInputStream(bis) {
						@Override
						protected Class<?> resolveClass(ObjectStreamClass desc)
							throws IOException, ClassNotFoundException {
							String className = desc.getName();
							
							try {
								return Class.forName(className);
							} catch (ClassNotFoundException exc) {
								for (ClassLoader cl: model.getDynamicClassLoaders()) {
									try {
										System.out.println("class loader: " + cl);
										return cl.loadClass(className);
									} catch (ClassNotFoundException e) {}
								}
								throw new ClassNotFoundException(className);
							}
						}
					};
					
					model.clearCanvas();
					
					Object o = null;
					int size = ois.readInt();
					for (int i = 0; i < size; i++) {
						Object obj = ois.readObject();
						System.out.println(obj);
						Shape shape = (Shape) obj;
						//System.out.println("read:" + shape);
						AddShapeCommand cm = new AddShapeCommand(shape, 1);
						// model.addShape(shape);
						model.addCommand(cm);
					}
					
					ois.close();
				}
				catch (Exception ex) {
					ex.printStackTrace();
				}
			}
		}
	}
	
	private class SaveHandler implements ActionListener {
		public void actionPerformed(ActionEvent e) {
			JFileChooser fileChooser = new JFileChooser();
			FileFilter filter = new FileNameExtensionFilter(null, "mdr");
			fileChooser.setFileFilter(filter);
			
			int option = fileChooser.showSaveDialog(getParent());
			if (option == JFileChooser.APPROVE_OPTION) {
			
				String path = fileChooser.getSelectedFile().getAbsolutePath();
				
				if (!path.endsWith(".mdr")) {
					path = path + ".mdr";
				}
				
				try {
					FileOutputStream fos = new FileOutputStream(path);
					ObjectOutputStream oos = new ObjectOutputStream(fos);
	
					List<Shape> shapeList = model.getShapes();
					
					oos.writeInt(shapeList.size());
					for (Shape shape : shapeList) {
						oos.writeObject(shape);
					}
					
					oos.close();
				}
				catch (Exception ex) {
					ex.printStackTrace();
				}
				
			}
		}
	}
	
	private void updateToolBar() {
		toolBar.removeAll();
		toolMenu.removeAll();
		
		for (Object tc: appToolList) {
			toolBar.add((Action)tc);
			toolMenu.add((Action)tc);
		}
		
		for (Object tc: dynToolList) {
			toolBar.add((Action)tc);
			toolMenu.add((Action)tc);
		}
		
		
		repaint();
		validate();
	}
	
	private class ShareToolMenuHandler implements ActionListener {
		public void actionPerformed(ActionEvent e) {
			ShareToolDialog dialog = new ShareToolDialog(null, model);
			dialog.setLocationRelativeTo(null);
			boolean result = dialog.showDialog();
			
			if (result == true) {
				System.out.println("Selected tools: ");
				ArrayList<ToolInfo> selectedTools = dialog.getSelectedTools();
				ArrayList<ToolArchive> toolsToSend = new ArrayList<ToolArchive>();
				
				for (ToolInfo t: selectedTools) {
					System.out.println(t);
					ToolArchive ta = ToolArchive.buildToolArchive(t, model.getName());
					if (ta != null)
						toolsToSend.add(ta);
				}
				
				if (toolsToSend.size() > 0) {
					AddToolCommand cmd = new AddToolCommand(toolsToSend, model.getName());
					model.addCommand(cmd);
				}
				
				
			}
		}
	}

	@Override
	public void updateTools() {
		// TODO Auto-generated method stub
		Vector<Object> tools = model.getSessionToolLoader().reloadSessionToolList(model, canvas);
		
		dynToolList = tools;
		
		updateToolBar();
	}
	
	/****< AppCloser >***********************************************************/

	  /**
	   * Inner class for terminating the application.
	   *
	   * When executed as an application, closing the window does not necessarily
	   * trigger application termination. This class catches the window closing
	   * event and terminates the application.
	   */
	  static class AppCloser extends WindowAdapter  {
		  MultiDraw1 multidraw;
		  public AppCloser(MultiDraw1 multidraw) {
			  this.multidraw = multidraw;
		  }
	    public void windowClosing(WindowEvent e) {
	    	multidraw.shutdown();
	        System.exit(0);
	    }
	  }// end static class AppCloser extends WindowAdapter
	  
	  private class AddToolMenuHandler implements ActionListener {
			public void actionPerformed(ActionEvent e) {
				JFileChooser fileChooser = new JFileChooser();
				fileChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
			    //
			    // disable the "All files" option.
			    //
				fileChooser.setAcceptAllFileFilterUsed(false);
				
				int option = fileChooser.showOpenDialog(getParent());
				if (option == JFileChooser.APPROVE_OPTION) {
					try {
						//do sth to load the tool
						System.out.println(fileChooser.getCurrentDirectory());
						System.out.println(fileChooser.getSelectedFile());
						Vector<Object> newtools = model.getSessionToolLoader().addNewTool(fileChooser.getSelectedFile(), model, canvas);
						
						//in case of local mode, then update tool bar immediately
						if (model.getSessionID() == null) {
							dynToolList.addAll(newtools);
							updateToolBar();
						}						
						//in case of online mode, user have to share the tool to make it to the tool bar.
					}
					catch (Exception ex) {
						ex.printStackTrace();
					}
				}
			}
		}
	  
	  public void shutdown() {
		  model.logout();
	  }

	@Override
	public void updateUserStatus() {
		if (model.getSessionID() != null && !model.getName().equals(model.getController())) {
			model.setCurrentTool(null);
			for (Object o : appToolList) {
				((AbstractAction) o).setEnabled(false);
			}
			for (Object o : dynToolList) {
				((AbstractAction) o).setEnabled(false);
			}
			toolMenu.setEnabled(false);
		}
		else {
			for (Object o : appToolList) {
				((AbstractAction) o).setEnabled(true);
			}
			for (Object o : dynToolList) {
				((AbstractAction) o).setEnabled(true);
			}
			toolMenu.setEnabled(true);
		}
	}
}

